package com.company.剑指offer每日刷题.专项版;

import java.util.HashMap;
import java.util.Map;

/**
 * 剑指 Offer II 011. 0 和 1 个数相同的子数组
 * 给定一个二进制数组 nums , 找到含有相同数量的 0 和 1 的最长连续子数组，并返回该子数组的长度。
 * 示例 1:
 * <p>
 * 输入: nums = [0,1]
 * 输出: 2
 * 说明: [0, 1] 是具有相同数量 0 和 1 的最长连续子数组。
 */
public class FindMaxLength {

    /**
     * 前缀法
     * 思路：
     *      设定一个初始值为sum=0,将一段子数组中的所有元素进行遍历，如果遇0,看成-1加之，如果遇1，则直接加之,这样一来，这个子数组中的0,1个数如果相同，那么
     * 子数组中的所有元素总和为0。就将判断一段子数组中0、1的个数相同的问题，转化成了判断一段子数组的总和是否为0的问题。
     *      按照上面的思路，我们将[n1,n2,n3,...nn]按照上面的法则进行前缀和计算，得到了[S1,S2,S3,....,Sn]。 假设下标为i到j的元素0，1个数相同的，可以得出这样关系：
     * Sj - Si-1==0 => Sj == Si-1，长度len = j-i+1
     * 优化：为了快速找到那个最长子数组的起点，map的key 存储前缀和，而value存储值为key的第一个前缀和的下标。
     * 与《和为k的子数组》一题很类似。
     * <p>
     * Sj - Si = 0 ，len = j - i, Sj= Si
     */
    public int findMaxLength(int[] nums) {
        int maxLen = 0;
        int sum = 0;
        Map<Integer, Integer> map = new HashMap<>();
        map.put(0, -1);
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] == 0) {
                sum--;
            } else {
                sum++;
            }
            if (map.get(sum) == null) {
                map.put(sum, i);
            } else {
                maxLen = Math.max(i - map.get(sum), maxLen);
            }
        }
        return maxLen;
    }

    //              -1, 0,1,2
//0,1,0  [-1,1,-1]  [0,-1,0,-1]
    public static void main(String[] args) {
        FindMaxLength obj = new FindMaxLength();
        int maxLength = obj.findMaxLength(new int[]{0, 1, 0});
        System.out.println(maxLength);
    }
}
